package com.lzw.DailyAudit.service.issueReports_04;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.hooya.fa.eu.biz.DailyAudit.entity.CheckProfitBO;
import com.hooya.fa.eu.biz.DailyAudit.entity.DailyAuditStatus;
import com.hooya.fa.eu.biz.DailyAudit.entity.ProfitCheckRecord;
import com.hooya.fa.eu.biz.DailyAudit.entity.ProfitStatement;
import com.hooya.fa.eu.biz.DailyAudit.entity.dto.ProfitCheckDTO;
import com.hooya.fa.eu.biz.DailyAudit.entity.dto.ProfitStatementDaily;
import com.hooya.fa.eu.biz.DailyAudit.mapper.*;
import com.hooya.fa.eu.biz.DailyAudit.service.dateAggregation_02.CommonService;
import com.xxl.job.core.context.XxlJobHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ProfitCheck {

	@Autowired
	private DailyStkOrigDataCollectMapper dailyStkOrigDataCollectMapper;

	@Autowired
	private DailyMetaExchangePlMapper dailyMetaExchangePlMapper;

	@Autowired
	private CommonService commonService;

	@Autowired
	private DailyAuditStatusMapper statusMapper;

	@Autowired
	private DailyProfitCheckMapper profitCheckMapper;

	@Autowired
	private DailyTempKcjlDailyMapper tempKcjlDailyMapper;

	private static final AtomicLong ID_GENERATOR = new AtomicLong(System.currentTimeMillis() * 100000);


	private static final String[] SECOND_COLUMNS = {"index", "SKU", "invoice_number", "invoice_date", "inbound_date", "begin_of_the_month_stock", "end_of_the_month_stock", "inbound", "sales", "transfer", "lost", "restock", "in_price_eur", "in_price_usd", "currency_rate", "PLN", "type", "flag", "level_0", "in_price_usd_x", "transfer_y"};

	private static final String[] PROFIT_COLUMNS = {"SKU", "invoice_number", "invoice_date", "inbound_date", "begin_of_the_month_stock", "end_of_the_month_stock", "inbound", "sales", "transfer", "lost", "restock", "in_price_eur", "in_price_usd", "currency_rate", "PLN", "type", "flag", "belongs"};

	private static final List<String> COMPANY_LIST = Arrays.asList("costway-fr", "costway-it", "costway-pl", "costway-de");

	private static final String PATH = "./profit_check/";


	/**
	 * 主方法
	 */
	public void profitCheck(String start) {
		try {

			String name = "profitCheck-04";
			DailyAuditStatus dailyAuditStatus = statusMapper.selectStatus(name, start);
			if (dailyAuditStatus != null && dailyAuditStatus.getFlag() == 1) {
				log.info("{} - 该方法已执行，进行跳过！", name);
				return;
			}

			//获取明天的时间作为截止时间
			LocalDate endDate = LocalDate.parse(start, DateTimeFormatter.ofPattern("yyyy-MM-dd")).plusDays(1); // 如start:08-01  则endDate:08-02
			String end = endDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); //08-02
			String currDate = start.replace("-", ""); //20250801  那月和日拼接

			String dir = commonService.joinDate(PATH, start);
			// 创建输出目录
			commonService.createDirectories(dir);
			//创建接收_sub的数据集合
			List<ProfitCheckDTO> allData = new ArrayList<>();
			//创建接收处理后的数据集合，并进行数据的自动化处理
			List<ProfitCheckDTO> result = new ArrayList<>();

			// 第一流程 -- 完成   示例： 这里会读取profit_check/xx年/xx月/xx日/costway-de_20251001.csv
			for (String company : COMPANY_LIST) {
				List<ProfitCheckDTO> reduceOutData = firstFlow(start, end, company);  // 处理数据
				allData.addAll(reduceOutData);
			}

			// 第二流程 -- 完成
			for (String company : COMPANY_LIST) {
				List<ProfitCheckDTO> afterDealData = secondFlow(allData, company);  // 整理数据
				result.addAll(afterDealData);
			}

			// 报表自动化处理
			result = DealReportAutomatic(result);

			//根据不同公司产出报表
			for (String company : COMPANY_LIST) {
				log.info(company);
				String savePath = dir + "/" + company + "_" + currDate + ".csv";
				List<ProfitCheckDTO> collect = result.stream().filter(f -> company.equalsIgnoreCase(f.getCompany())).collect(Collectors.toList());
				// 分公司 - 将数据写入CSV文件
				writeSecondFlowCsvFile(collect, savePath);
			}

			// 修改执行状态
			commonService.UpdateOrInsertStatus(dailyAuditStatus, name, start);

			log.info("利润检查表生成完毕!");
			XxlJobHelper.log("利润检查表生成完毕");
		} catch (Exception e) {
			log.error("处理ProfitCheck数据时发生错误", e);
			XxlJobHelper.log("处理ProfitCheck数据时发生错误");
			throw new RuntimeException("处理ProfitCheck数据时发生错误", e);
		}
	}

	private List<ProfitCheckDTO> DealReportAutomatic(List<ProfitCheckDTO> result) {
		//处理报表数据自动化 - 此时所有的数据都在result中，我们现在就不必区分公司了，直接处理整体的数据，最后做一个区分即可
		List<ProfitCheckDTO> res = result.stream().filter(f -> StrUtil.isBlank(f.getFlag())).collect(Collectors.toList());
		List<ProfitCheckDTO> dealData = result.stream().filter(f -> StrUtil.isNotBlank(f.getFlag())).collect(Collectors.toList());
		if (CollectionUtil.isNotEmpty(dealData)) {

			// 1,为相同的数据添加索引 - 用于区分转运的数据
			Map<String, String> indexMap = new HashMap<>();
			int i = 1;
			for (ProfitCheckDTO dealDatum : dealData) {
				String index = dealDatum.getSku() + "|" + dealDatum.getInvoiceNumber() + "|" + dealDatum.getInboundDate();
				if (indexMap.containsKey(index)) {
					dealDatum.setIndex(indexMap.get(index));
				} else {
					indexMap.put(index, i + "");
					dealDatum.setIndex(indexMap.get(index));
					i++;
				}
			}

			// 2,根据sku进行分组，并处理分组后的数据
			Map<String, List<ProfitCheckDTO>> collect = dealData.stream().collect(Collectors.groupingBy(ProfitCheckDTO::getSku));
			List<List<ProfitCheckDTO>> DataList = new ArrayList<>(collect.values());
			for (List<ProfitCheckDTO> List : DataList) {

				// 这个外层是同sku不同index的数据组
				List.sort(Comparator.comparing(ProfitCheckDTO::getIndex)); // 根据index排序+transfer进行排序
				// 获取分组数量 - 查看index的分组数量 - collect1 是同类sku的同一index的数据组 因为这个数据组必须一组一组进行处理
				Map<String, List<ProfitCheckDTO>> collect1 = List.stream().collect(Collectors.groupingBy(ProfitCheckDTO::getIndex));
				long groupCount = collect1.size();
				if (groupCount == 1) {
					// 当只有1组数据时
					AtomicInteger AllQty = new AtomicInteger(List.get(0).getInbound());
					AtomicInteger AllTransferQty = new AtomicInteger(0);
					AtomicInteger VerifyQty = new AtomicInteger(0);
					AtomicInteger AllSaleQty = new AtomicInteger(List.get(0).getSales() == null ? 0 : List.get(0).getSales());// 当前组的sale数据
					List.forEach(f -> AllTransferQty.addAndGet(f.getTransfer()));
					List.forEach(f -> {

						//1, 每次处理inbound数据之前先更新当前剩余的总量
						f.setInbound(AllQty.get());
						if (f.getInbound() >= f.getTransfer()) {
							//2,入仓大于转运时，入仓设置为转运，且剩余的总量减去转运数据
							f.setInbound(f.getTransfer());
							//3,总量减去入仓数据
							AllQty.addAndGet(-f.getTransfer());
							//4，处理sale数据
							dealSaleData(f, AllSaleQty);
							//5,更新end数据
							updateEndOfMonthStock(f);
							//6，更新校验数据总量
							VerifyQty.addAndGet(f.getInbound());
							//7,添加此条数据
							res.add(f);
						} else {
							//出现总量小于转运数据，则出现错误，正常来讲，入仓的总量 = 转运的总量 所以入仓的总量应该总是大于等于转运的总量
							log.error("错误数据，他的信息为{}：", f);
							throw new RuntimeException("处理报表自动化时发生错误");
						}

					});
					// 校验 当前的sku的入仓数据量和转运数量是否一致
					if (VerifyQty.get() != AllTransferQty.get()) {
						log.error("处理报表自动化时发生错误,转运数据总量与入仓数据数据总量不一致！该条数据集为：{}", List);
						throw new RuntimeException("处理报表自动化时发生错误,转运数据总量与入仓数据数据总量不一致！");
					}

				} else {
					// 当有多组数据时 - 这个外层是同sku不同index的数据组

					ArrayList<List<ProfitCheckDTO>> l = new ArrayList<>(collect1.values()); // l  他里面的数据就是拿到同sku同index的每组数据，通过遍历这个l的数据进行数据的处理

					//记录当前的扣减的转运是哪一个，以及当前的剩余数量
					AtomicInteger transfer = new AtomicInteger(0);
					AtomicInteger transferQty = new AtomicInteger(0);
					AtomicInteger AllTransferQty = new AtomicInteger(0);
					AtomicInteger VerifyQty = new AtomicInteger(0);

					//添加转运数据总量
					l.get(0).forEach(f -> AllTransferQty.addAndGet(f.getTransfer()));

					//转运使用完成的标记后续可能还有做一个修改，去判断具体的数据位置，位置判断+数值判断 一般来说排序完成后，位置判断足够，但是多一层判断
					HashMap<Integer, Integer> transferMap = new HashMap<>();

					l.forEach(DealDataList -> {
						AtomicInteger count = new AtomicInteger(0);
						AtomicInteger AllQty = new AtomicInteger(DealDataList.get(0).getInbound());// 获取每一组的总的inbound值，这代表了这一组一共的入仓数据
						int sales = DealDataList.get(0).getSales() == null ? 0 : DealDataList.get(0).getSales(); // 当前组的sale数据
						AtomicInteger saleQty = new AtomicInteger(sales);
						for (ProfitCheckDTO data : DealDataList) {
							// 自增1，记录对应的数据位置
							count.incrementAndGet();

							// 如果这个数据的位置对应上了排除的位置 并且 这个位置的数据也对应上了排除的数值，则这数据就不需要处理了，直接跳过
							// TODO - 这里其实是有bug的，一旦每组相同sku数据集和转运顺序不一致，就会导致程序扣减的失败，处理的方法也很简单，就是根据转运值进行排序一下就好，但是当前的业务逻辑是按照默认的顺序处理数据（即，好似不允许进行排序）- 最主要的是排序的业务不确定和条件缺失
							if (!transferMap.isEmpty() && transferMap.containsKey(count.get()) && Objects.equals(data.getTransfer(), transferMap.get(count.get()))) {
								continue;
							}

							//处理转运和入仓的数据
							if (transferQty.get() == 0) {
								// 说明是第一次执行到这个位置 - 记录当前正在执行哪一个转运数据和当前转运的剩余量
								transfer.set(data.getTransfer());
								transferQty.set(data.getTransfer());
							}

							if (AllQty.get() < transferQty.get()) {
								//1,设置当前的inbound数据
								data.setInbound(AllQty.get());
								//2,处理完成inbound数据后，在处理sale数据
								dealSaleData(data, saleQty);
								//3,更新end数据
								updateEndOfMonthStock(data);
								//4，更新校验数据总量
								VerifyQty.addAndGet(data.getInbound());
								//5，添加本条记录
								res.add(data);
								//6,扣减当前的转运数量
								transferQty.addAndGet(-AllQty.get());
								break; // 跳出整个循环 - 应为这个数据处理完成
							} else {
								//1,设置inbound数据
								data.setInbound(transferQty.get());
								//2，将转运的值置0
								transferQty.addAndGet(-transferQty.get()); // 将转运数据扣减为0
								//3，扣减当前的inbound总量数据
								AllQty.addAndGet(-data.getInbound());
								//4，添加转运使用完毕的标识，因为使用完了，所以后续在有这个内容，就必须跳过
								transferMap.put(count.get(), transfer.get());
								//5,处理完成inbound值后，在处理sale数据值
								dealSaleData(data, saleQty);
								//6,更新end数据
								updateEndOfMonthStock(data);
								//7，更新校验数据总量
								VerifyQty.addAndGet(data.getInbound());
								//8，添加本条数据
								res.add(data);
							}


						}
					});
					// 校验 当前的sku的入仓数据量和转运数量是否一致
					if (VerifyQty.get() != AllTransferQty.get()) {
						log.error("处理报表自动化时发生错误,转运数据总量与入仓数据数据总量不一致！该条数据集为：{}", List);
						throw new RuntimeException("处理报表自动化时发生错误,转运数据总量与入仓数据数据总量不一致！");
					}

				}


			}


		}
		return res;
	}

	/**
	 * 计算月末库存数据
	 *
	 * @param data
	 */
	private static void updateEndOfMonthStock(ProfitCheckDTO data) {
		int beginOfMonthStock = data.getBeginOfMonthStock() != null ? data.getBeginOfMonthStock() : 0;
		int inbound = data.getInbound() != null ? data.getInbound() : 0;
		int sale = data.getSales() != null ? data.getSales() : 0;
		int transfer_x = data.getTransfer_x() != null ? data.getTransfer_x() : 0;
		int lost = data.getLost() != null ? data.getLost() : 0;
		int restock = data.getRestock() != null ? data.getRestock() : 0;
		data.setEndOfMonthStock(beginOfMonthStock + inbound - sale - transfer_x - lost + restock);
	}

	/**
	 * 处理销售数据
	 *
	 * @param data
	 * @param saleQty
	 */
	private static void dealSaleData(ProfitCheckDTO data, AtomicInteger saleQty) {
		if (saleQty.get() < 0) {
			log.error("错误数据，他的信息为{}：", data);
			throw new RuntimeException("处理报表自动化时发生错误,出现销售数据为负数！");
		}
		if (saleQty.get() == 0) {
			data.setSales(null);
		} else {
			if (data.getInbound() >= saleQty.get()) {
				// 当入仓的数量 > 销售的数量 则认为可以销售所有数据
				data.setSales(saleQty.get());
				// 销售设置完成后，将销售总量置为0
				saleQty.addAndGet(-saleQty.get());
			} else {
				// 当入仓的数量 < 销售的数量 则认为只能销售入仓的数据
				data.setSales(data.getInbound());
				// 销售总量 - 入仓数据（入仓销售的值）
				saleQty.addAndGet(-data.getInbound());
			}
		}


	}

	/**
	 * 第一流程处理
	 */
	private List<ProfitCheckDTO> firstFlow(String start, String end, String company) {
		// 上个月数据
		List<ProfitCheckDTO> prevData = getPrevData(start, company, PATH);  // 对于这些cvs数据，可以考虑设计数据库存储，数据存储到数据库中方便读写

		// 当月采购、报损、b2b数据
		List<ProfitCheckDTO> inData = unitInData(start, end, company);

		Map<String, List<ProfitCheckDTO>> dealPriceMap = dealPrice(inData);
		inData = dealPriceMap.get("priceData");
		List<ProfitCheckDTO> noPriceData = dealPriceMap.get("noPriceData");

		if (CollectionUtil.isNotEmpty(noPriceData)) {
			log.error("存在没有价格的数据");
			throw new RuntimeException("存在没有价格数据");
		}

		List<ProfitCheckDTO> allInData = new ArrayList<>(prevData);
		allInData.addAll(inData);

		// 转换日期格式 -- 将其统一为 yyyy-MM-dd -- 用于排序
		changeDateFormatter(allInData);


		List<ProfitCheckDTO> reduceOutData = dealOutData(allInData, start, end, company);
		reduceOutData.forEach(f -> {
			f.setCompany(company);
			f.setInbound(f.getInbound() == null ? 0 : f.getInbound());
			f.setSales(f.getSales() == null ? 0 : f.getSales());
			f.setLost(f.getLost() == null ? 0 : f.getLost());
			f.setRestock(f.getRestock() == null ? 0 : f.getRestock());
			f.setTransfer(f.getTransfer() == null ? 0 : f.getTransfer());
			f.setCreatetime(Date.from(LocalDateTime.now().atZone(java.time.ZoneId.systemDefault()).toInstant()));
		});

		return reduceOutData;
	}

	private static void changeDateFormatter(List<ProfitCheckDTO> allInData) {
		allInData.forEach(dto -> {
			String dateStr = dto.getInboundDate();
			if (dateStr == null || dateStr.isEmpty()) {
				return;
			}

			// 检查是否已经是 yyyy-MM-dd 格式
			DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd");
			try {
				LocalDate.parse(dateStr, formatter1);
				// 已经是正确格式，不修改
				return;
			} catch (DateTimeParseException e) {
				// 不是 yyyy-MM-dd 格式，尝试解析 yyyy/M/d
			}

			// 尝试解析 yyyy/M/d 格式
			DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy/M/d");
			try {
				LocalDate parsedDate = LocalDate.parse(dateStr, formatter2);
				dto.setInboundDate(parsedDate.format(formatter1)); // 转换为 yyyy-MM-dd
			} catch (DateTimeParseException e) {
				log.warn("无法解析日期: {}", dateStr);
			}
		});
	}

	/**
	 * 获取前一天的数据
	 */
	private List<ProfitCheckDTO> getPrevData(String start, String company, String path) {
		// 获取前一天
		String prevLast = commonService.getPrevDate(start);
		String dir = commonService.joinDate(path, prevLast);

		String prevDate = prevLast.replace("-", "");  // 那对应报表的数据 - 20250731
		String dataPath = dir + "/" + company + "_" + prevDate + ".csv";

		List<ProfitCheckDTO> prevData = new ArrayList<>();
		try (BufferedReader br = new BufferedReader(new InputStreamReader(Files.newInputStream(Paths.get(dataPath)), "GBK"))) {
			String line;
			String[] headers = null;
			int lineCount = 0;

			while ((line = br.readLine()) != null) {
				String[] values = line.split(",");

				// 第一行是标题
				if (lineCount == 0) {
					headers = values;
					lineCount++;
					continue;
				}

				// 创建一个Map来存储标题和值的对应关系
				Map<String, String> rowMap = new HashMap<>();
				for (int i = 0; i < headers.length && i < values.length; i++) {
					rowMap.put(headers[i].trim(), values[i].trim());
				}

				// 只处理 end_of_the_month_stock > 0 的数据
				String endOfMonthStockStr = rowMap.get("end_of_the_month_stock");
				if (endOfMonthStockStr != null && !endOfMonthStockStr.isEmpty()) {
					try {
						// 保存原始字符串用于日志记录
						int endOfMonthStock = Integer.parseInt(commonService.cleanString(endOfMonthStockStr));

						if (endOfMonthStock > 0) {
							ProfitCheckDTO dto = new ProfitCheckDTO();

							// 设置需要的字段
							dto.setSku(rowMap.get("SKU"));
							dto.setInvoiceNumber(rowMap.get("invoice_number"));
							dto.setInvoiceDate(rowMap.get("invoice_date"));
							dto.setInboundDate(rowMap.get("inbound_date"));
							dto.setEndOfMonthStock(endOfMonthStock);

							// 安全地解析Double值，处理空字符串情况
							String currencyRateStr = rowMap.get("currency_rate");
							if (currencyRateStr != null && !currencyRateStr.isEmpty() && !"null".equals(currencyRateStr)) {
								dto.setCurrencyRate(Double.valueOf(currencyRateStr));
							} else {
								dto.setCurrencyRate(null);
							}

							String plnStr = rowMap.get("PLN");
							if (plnStr != null && !plnStr.isEmpty() && !"null".equals(plnStr)) {
								dto.setPln(Double.valueOf(plnStr));
							} else {
								dto.setPln(null);
							}

							dto.setType(rowMap.get("type"));

							// 将 end_of_the_month_stock 赋值给 begin_of_the_month_stock
							dto.setBeginOfMonthStock(endOfMonthStock);

							// 设置价格相关信息
							Double inPriceEur = commonService.parseDoubleValue(rowMap.get("in_price_eur"));
							Double inPriceUsd = commonService.parseDoubleValue(rowMap.get("in_price_usd"));

							// 当 in_price_eur 不为 null 时，in_price_usd 设为 null
							if (inPriceEur != null) {
								inPriceUsd = null;
							}
							dto.setInPriceEur(inPriceEur);
							dto.setInPriceUsd(inPriceUsd);

							// 设置其他字段为0
							dto.setInbound(0);
							dto.setSales(0);
							dto.setRestock(0);
							dto.setLost(0);
							dto.setTransfer(0);

							prevData.add(dto);
						}
					} catch (NumberFormatException e) {
						log.warn("解析end_of_the_month_stock数值时出错: {}，原始字符串: '{}'", endOfMonthStockStr, rowMap.get("end_of_the_month_stock"), e);
					}
				}

				lineCount++;
			}
		} catch (IOException e) {
			log.error("读取前一个月数据文件时出错: {}", dataPath, e);
		}

		return prevData;
	}

	/**
	 * 整合入库数据
	 */
	private List<ProfitCheckDTO> unitInData(String start, String end, String company) {
		List<ProfitCheckDTO> cgIn = dailyStkOrigDataCollectMapper.getCgInData(start, end, company);
		if (CollectionUtil.isNotEmpty(cgIn)) {
			cgIn.forEach(f -> {
				f.setBeginOfMonthStock(0);
				f.setEndOfMonthStock(f.getInbound());
				f.setSales(0);
				f.setLost(0);
				f.setTransfer(0);
				f.setExchangedate(commonService.getPrevDate(f.getInvoiceDate()));
			});
		}

		List<ProfitCheckDTO> bsIn = dailyStkOrigDataCollectMapper.getBsInData(start, end, company);
		if (CollectionUtil.isNotEmpty(bsIn)) {
			List<ProfitCheckDTO> processedBsIn = new ArrayList<>();
			// 按SKU、inboundDate、price、currency、type分组
			Map<String, List<ProfitCheckDTO>> groupedBsIn = bsIn.stream().collect(Collectors.groupingBy(dto -> dto.getSku() + "|" + dto.getInboundDate() + "|" + dto.getPrice() + "|" + dto.getCurrency() + "|" + dto.getType()));

			// 对每组数据进行聚合处理
			for (Map.Entry<String, List<ProfitCheckDTO>> entry : groupedBsIn.entrySet()) {
				List<ProfitCheckDTO> group = entry.getValue();
				if (!group.isEmpty()) {
					// 创建新的DTO对象
					ProfitCheckDTO aggregatedDto = new ProfitCheckDTO();
					BeanUtil.copyProperties(group.get(0), aggregatedDto);

					// 计算聚合值（对restock求和）
					int totalRestock = group.stream().mapToInt(ProfitCheckDTO::getRestock).sum();

					// 设置聚合后的值
					aggregatedDto.setRestock(totalRestock);
					aggregatedDto.setBeginOfMonthStock(0);
					aggregatedDto.setEndOfMonthStock(totalRestock);
					aggregatedDto.setSales(0);
					aggregatedDto.setLost(0);
					aggregatedDto.setTransfer(0);
					aggregatedDto.setExchangedate(commonService.getPrevDate(aggregatedDto.getInboundDate()));
					processedBsIn.add(aggregatedDto);
				}
			}
			bsIn = processedBsIn;
		}

		List<ProfitCheckDTO> b2bIn = dailyStkOrigDataCollectMapper.getB2bInData(start, end, company);
		b2bIn.forEach(f -> {
			f.setExchangedate(commonService.getPrevDate(end));
			f.setFlag("");
		});

		List<ProfitCheckDTO> zyIn = dailyStkOrigDataCollectMapper.getzyInData(start, end, company);
		List<ProfitCheckDTO> B2bAndZyIn = new ArrayList<>(b2bIn);
		B2bAndZyIn.addAll(zyIn);
		if (CollectionUtil.isNotEmpty(B2bAndZyIn)) {
			List<ProfitCheckDTO> processedB2bAndZyIn = new ArrayList<>();
			// 按SKU、inboundDate、price、currency、type分组
			Map<String, List<ProfitCheckDTO>> groupedB2bAndZyIn = B2bAndZyIn.stream().collect(Collectors.groupingBy(dto -> dto.getSku() + "|" + dto.getInboundDate() + "|" + dto.getPrice() + "|" + dto.getInvoiceNumber() + "|" + dto.getInvoiceDate() + "|" + dto.getCurrency() + "|" + dto.getType() + "|" + dto.getExchangedate() + "|" + dto.getFlag()));

			// 对每组数据进行聚合处理
			for (Map.Entry<String, List<ProfitCheckDTO>> entry : groupedB2bAndZyIn.entrySet()) {
				List<ProfitCheckDTO> group = entry.getValue();
				if (!group.isEmpty()) {
					// 创建新的DTO对象
					ProfitCheckDTO aggregatedDto = new ProfitCheckDTO();
					BeanUtil.copyProperties(group.get(0), aggregatedDto);

					// 计算聚合值（对restock求和）
					int totalInbound = group.stream().mapToInt(ProfitCheckDTO::getInbound).sum();

					// 设置聚合后的值
					aggregatedDto.setInbound(totalInbound);
					aggregatedDto.setBeginOfMonthStock(0);
					aggregatedDto.setEndOfMonthStock(totalInbound);
					aggregatedDto.setSales(0);
					aggregatedDto.setLost(0);
					aggregatedDto.setTransfer(0);
					processedB2bAndZyIn.add(aggregatedDto);
				}
			}
			B2bAndZyIn = processedB2bAndZyIn;
		}


		List<ProfitCheckDTO> inData = new ArrayList<>(cgIn);
		inData.addAll(bsIn);
		inData.addAll(B2bAndZyIn);

		return inData;
	}

	/**
	 * 处理价格数据
	 */
	private Map<String, List<ProfitCheckDTO>> dealPrice(List<ProfitCheckDTO> data) {
		Map<String, List<ProfitCheckDTO>> result = new HashMap<>();

		// 获取汇率数据
		List<ProfitCheckDTO> exchangeList = dailyMetaExchangePlMapper.getDataForProFitCheck();

		List<ProfitCheckDTO> priceData = new ArrayList<>();
		List<ProfitCheckDTO> noPriceData = new ArrayList<>();
		// 分离有价格和无价格的数据
		for (ProfitCheckDTO item : data) {
			if (item.getPrice() == null) {
				noPriceData.add(item);
			} else {
				priceData.add(item);
			}
		}

		// 检查有价格的数据中currency是否为null
		boolean hasNullCurrency = priceData.stream().anyMatch(dto -> dto.getCurrency() == null || dto.getCurrency().isEmpty());
		if (hasNullCurrency) {
			log.error("价格数据中存在currency为空的数据");
			throw new IllegalArgumentException("价格数据中存在currency为空的情况");
		}
		// 打印无价格数据信息（如果存在）
		if (!noPriceData.isEmpty()) {
			log.info("发现{}条无进仓价格的数据,数据为{}", noPriceData.size(), noPriceData);
		}

		Map<String, List<ProfitCheckDTO>> exchangeListMap = exchangeList.stream().collect(Collectors.groupingBy(ProfitCheckDTO::getRatedate));

		List<ProfitCheckDTO> eurData = priceData.stream().filter(f -> "EUR".equals(f.getCurrency())).collect(Collectors.toList());
		if (CollectionUtil.isNotEmpty(eurData)) {
			List<ProfitCheckDTO> withDescription = new ArrayList<>();
			for (ProfitCheckDTO mainItem : eurData) {
				String key = mainItem.getExchangedate();
				if (exchangeListMap.containsKey(key)) {
					List<ProfitCheckDTO> ProfitCheckDTOS = exchangeListMap.get(key);
					ProfitCheckDTOS.forEach(f -> {
						ProfitCheckDTO newItem = new ProfitCheckDTO();
						BeanUtil.copyProperties(mainItem, newItem);
						newItem.setUsdplnrate(f.getUsdplnrate());
						newItem.setEurplnrate(f.getEurplnrate());
						double pln = Math.round(newItem.getPrice() * newItem.getEurplnrate() * 100.0) / 100.0;
						newItem.setPln(pln);
						newItem.setInPriceUsd(null);
						newItem.setCurrencyRate(newItem.getEurplnrate());
						newItem.setInPriceEur(newItem.getPrice());
						newItem.setUsdplnrate(null);
						newItem.setRatedate(null);
						withDescription.add(newItem);
					});
				} else {
					withDescription.add(mainItem);
				}
			}
			eurData = withDescription;
		}

		List<ProfitCheckDTO> usdData = priceData.stream().filter(f -> "USD".equals(f.getCurrency())).collect(Collectors.toList());
		if (CollectionUtil.isNotEmpty(usdData)) {
			List<ProfitCheckDTO> withDescription = new ArrayList<>();
			for (ProfitCheckDTO mainItem : usdData) {
				String key = mainItem.getExchangedate();
				if (exchangeListMap.containsKey(key)) {
					List<ProfitCheckDTO> ProfitCheckDTOS = exchangeListMap.get(key);
					ProfitCheckDTOS.forEach(f -> {
						ProfitCheckDTO newItem = new ProfitCheckDTO();
						BeanUtil.copyProperties(mainItem, newItem);
						newItem.setUsdplnrate(f.getUsdplnrate());
						newItem.setEurplnrate(f.getEurplnrate());
						double pln = Math.round(newItem.getPrice() * newItem.getUsdplnrate() * 100.0) / 100.0;
						newItem.setPln(pln);
						newItem.setInPriceEur(null);
						newItem.setCurrencyRate(newItem.getUsdplnrate());
						newItem.setInPriceUsd(newItem.getPrice());
						newItem.setEurplnrate(null);
						newItem.setRatedate(null);
						withDescription.add(newItem);
					});
				} else {
					withDescription.add(mainItem);
				}
			}
			usdData = withDescription;
		}

		List<ProfitCheckDTO> plnData = priceData.stream().filter(f -> "PLN".equals(f.getCurrency())).collect(Collectors.toList());
		if (CollectionUtil.isNotEmpty(plnData)) {
			plnData.forEach(f -> {
				f.setInPriceEur(null);
				f.setInPriceUsd(null);
				f.setExchangedate(null);
				f.setPln(f.getPrice());
			});
		}

		List<ProfitCheckDTO> addExchangeData = new ArrayList<>(eurData);
		addExchangeData.addAll(usdData);
		addExchangeData.addAll(plnData);

		addExchangeData.forEach(f -> {
			f.setPrice(null);
			f.setExchangedate(null);
			f.setCurrency(null);
		});

		result.put("priceData", addExchangeData);
		result.put("noPriceData", noPriceData);
		return result;
	}

	/**
	 * 减少步骤处理
	 */
	private List<ProfitCheckDTO> reduceStep(List<ProfitCheckDTO> result, List<ProfitCheckDTO> outData, String reduceCol) {
		// 新的思路 - 这里的index好像可以使用id 来代表 因为后面的内容也是从他身上得来的
		for (int i = 0; i < result.size(); i++) {
			result.get(i).setIndex("" + i);
		}

		// 遍历出库数据
		for (ProfitCheckDTO row : outData) {
			String sku = row.getSku();
			int qty = row.getQty() == null ? 0 : row.getQty();

			// 按照优先级筛选不同类型的库存数据
			List<ProfitCheckDTO> resultBuySku = result.stream().filter(dto -> "买".equals(dto.getType()) && sku.equals(dto.getSku())).sorted(Comparator.comparing(ProfitCheckDTO::getInboundDate).thenComparing(ProfitCheckDTO::getInvoiceNumber)).collect(Collectors.toList());

			List<ProfitCheckDTO> resultPySku = result.stream().filter(dto -> "盘盈".equals(dto.getType()) && sku.equals(dto.getSku())).sorted(Comparator.comparing(ProfitCheckDTO::getInboundDate).thenComparing(ProfitCheckDTO::getInvoiceNumber)).collect(Collectors.toList());

			List<ProfitCheckDTO> resultBuySkuOnce = result.stream().filter(dto -> "一次性买".equals(dto.getType()) && sku.equals(dto.getSku())).sorted(Comparator.comparing(ProfitCheckDTO::getInboundDate).thenComparing(ProfitCheckDTO::getInvoiceNumber)).collect(Collectors.toList());

			List<ProfitCheckDTO> resultCgSku = result.stream().filter(dto -> "采购".equals(dto.getType()) && sku.equals(dto.getSku())).sorted(Comparator.comparing(ProfitCheckDTO::getInboundDate).thenComparing(ProfitCheckDTO::getInvoiceNumber)).collect(Collectors.toList());

			// 按优先级处理库存扣除：先扣除"买"来的货
			qty = deduct(result, reduceCol, qty, resultBuySku);
			// 再扣除"盘盈"的货
			qty = deduct(result, reduceCol, qty, resultPySku);
			// 再扣除"一次性买"的货
			qty = deduct(result, reduceCol, qty, resultBuySkuOnce);
			// 最后扣除"采购"的货
			deduct(result, reduceCol, qty, resultCgSku);
		}
		return result;
	}

	private int deduct(List<ProfitCheckDTO> result, String reduceCol, int qty, List<ProfitCheckDTO> arrayList) {
		boolean flag;
		flag = qty > 0;
		if (flag && !arrayList.isEmpty()) {
			for (ProfitCheckDTO row1 : arrayList) {
				//这里的index可以认为是他们的sku -- 如果sku唯一的话
				int endStock = row1.getEndOfMonthStock();
				String index = row1.getIndex();
				if (qty <= endStock) {
					for (ProfitCheckDTO f : result) {
						if (f.getIndex().equals(index)) {
							f.setEndOfMonthStock(f.getEndOfMonthStock() - qty);
							// 根据reduceCol参数增加相应字段的值
							if ("transfer".equals(reduceCol)) {
								f.setTransfer(f.getTransfer() + qty);
							} else if ("sales".equals(reduceCol)) {
								f.setSales(f.getSales() + qty);
							} else if ("lost".equals(reduceCol)) {
								f.setLost(f.getLost() + qty);
							}
							break; // 满足条件后跳出循环
						}
					}
					qty = 0;
				} else {
					for (ProfitCheckDTO f : result) {
						if (f.getIndex().equals(index)) {
							f.setEndOfMonthStock(0);
							// 根据reduceCol参数增加相应字段的值
							if ("transfer".equals(reduceCol)) {
								f.setTransfer(f.getTransfer() + endStock);
							} else if ("sales".equals(reduceCol)) {
								f.setSales(f.getSales() + endStock);
							} else if ("lost".equals(reduceCol)) {
								f.setLost(f.getLost() + endStock);
							}
							break; // 满足条件后跳出循环
						}
					}
					qty -= endStock;
				}
				if (qty == 0) break;
			}
		}
		return qty;
	}

	/**
	 * 处理出库数据
	 */
	private List<ProfitCheckDTO> dealOutData(List<ProfitCheckDTO> inData, String start, String end, String company) {
		List<ProfitCheckDTO> bsOut = dailyStkOrigDataCollectMapper.getBsOutData(start, end, company);
		List<ProfitCheckDTO> b2bOut = dailyStkOrigDataCollectMapper.getB2bOutData(start, end, company);
		List<ProfitCheckDTO> b2cOut = dailyStkOrigDataCollectMapper.getB2cOutData(start, end, company);
		List<ProfitCheckDTO> zyOut = dailyStkOrigDataCollectMapper.getZyOutData(start, end, company);

		List<ProfitCheckDTO> reduceZy = reduceStep(inData, zyOut, "transfer");
		List<ProfitCheckDTO> reduceB2c = reduceStep(reduceZy, b2cOut, "sales");
		List<ProfitCheckDTO> reduceB2b = reduceStep(reduceB2c, b2bOut, "sales");

		return reduceStep(reduceB2b, bsOut, "lost");
	}

	/**
	 * 第二流程处理
	 */
	private List<ProfitCheckDTO> secondFlow(List<ProfitCheckDTO> allData, String company) {
		// 处理转运数据 -- 这部分删除了company的值，暂时不管
		List<ProfitCheckDTO> zyData = allData.stream().filter(dto -> StrUtil.isNotBlank(dto.getFlag()) && company.equals(dto.getCompany())).map(f -> {
			// 创建新对象并复制属性
			ProfitCheckDTO newDto = new ProfitCheckDTO();
			BeanUtil.copyProperties(f, newDto);
			newDto.setIndex(f.getSku() + f.getInvoiceNumber() + f.getInboundDate() + f.getFlag());
			newDto.setCompany(null);
			return newDto;
		}).collect(Collectors.toList());


		// 这部分删除了company的值，暂时不管
		List<ProfitCheckDTO> nozyData = allData.stream().filter(dto -> StrUtil.isBlank(dto.getFlag()) && company.equals(dto.getCompany())).map(dto -> {
			// 创建新对象并复制属性
			ProfitCheckDTO newDto = new ProfitCheckDTO();
			BeanUtil.copyProperties(dto, newDto);
			newDto.setIndex(dto.getSku() + dto.getInvoiceNumber() + dto.getInboundDate());
			newDto.setCompany(null);
			newDto.setTransfer_x(newDto.getTransfer());  // 由于区分了x 和 y的区别，所以导致数据的本身默认都是_y，因为_x被单独拿出来了
			newDto.setTransfer(0);
			return newDto;
		}).collect(Collectors.toList());


		// 处理其他公司的数据
		List<ProfitCheckDTO> otherData = allData.stream().filter(dto -> dto.getTransfer() != null && dto.getTransfer() > 0 && !company.equals(dto.getCompany())).map(f -> {
			ProfitCheckDTO newItem = new ProfitCheckDTO();
			newItem.setSku(f.getSku());
			newItem.setTransfer(f.getTransfer());
			newItem.setInPriceEur(f.getInPriceEur());
			newItem.setInPriceUsd(f.getInPriceUsd());
			newItem.setCurrencyRate(f.getCurrencyRate());
			newItem.setCompany(f.getCompany());
			return newItem;
		}).collect(Collectors.toList());


		Map<String, List<ProfitCheckDTO>> descriptionDataMap = zyData.stream().collect(Collectors.groupingBy(f -> f.getSku() + f.getFlag()));
		List<ProfitCheckDTO> mergeData = new ArrayList<>();
		for (ProfitCheckDTO mainItem : otherData) {
			String key = mainItem.getSku() + mainItem.getCompany();
			if (descriptionDataMap.containsKey(key)) {
				List<ProfitCheckDTO> ProfitCheckDTOS = descriptionDataMap.get(key);
				ProfitCheckDTOS.forEach(f -> {
					ProfitCheckDTO newItem = new ProfitCheckDTO();
					BeanUtil.copyProperties(mainItem, newItem);
					newItem.setIndex(f.getIndex());
					newItem.setInvoiceNumber(f.getInvoiceNumber());
					newItem.setInvoiceDate(f.getInvoiceDate());
					newItem.setInboundDate(f.getInboundDate());
					newItem.setBeginOfMonthStock(f.getBeginOfMonthStock());
					newItem.setEndOfMonthStock(f.getEndOfMonthStock());
					newItem.setInbound(f.getInbound());
					newItem.setSales(f.getSales());
					newItem.setTransfer_x(f.getTransfer());
					newItem.setLost(f.getLost());
					newItem.setRestock(f.getRestock());
					newItem.setInPriceEur_x(f.getInPriceEur());
					newItem.setInPriceUsd_x(f.getInPriceUsd());
					newItem.setCurrencyRate_x(f.getCurrencyRate());
					newItem.setPln(f.getPln());
					newItem.setType(f.getType());
					newItem.setFlag(f.getFlag());
					mergeData.add(newItem);
				});
			} else {
				mergeData.add(mainItem);
			}
		}
		List<ProfitCheckDTO> newMergeData = mergeData.stream().filter(f -> f.getInvoiceNumber() != null && !f.getInvoiceNumber().isEmpty()).collect(Collectors.toList());


		List<ProfitCheckDTO> nodup = newMergeData.stream().collect(Collectors.groupingBy(ProfitCheckDTO::getIndex))  // 按index字段分组
				.entrySet().stream().filter(entry -> entry.getValue().size() == 1)  // 只保留只有一个元素的组（即没有重复的）
				.flatMap(entry -> entry.getValue().stream())   // 将组展平为单独的元素
				.collect(Collectors.toList());


		Set<String> nodupIndexSet = nodup.stream().map(ProfitCheckDTO::getIndex).collect(Collectors.toSet());
		List<ProfitCheckDTO> isdup = newMergeData.stream().filter(dto -> dto.getIndex() != null && !nodupIndexSet.contains(dto.getIndex())).collect(Collectors.toList());
		isdup.forEach(f -> f.setLevel_0(f.getIndex() + f.getTransfer() + f.getCurrencyRate()));


		List<ProfitCheckDTO> nodupZy;
		if (CollectionUtil.isEmpty(nodup)) {
			nodupZy = new ArrayList<>();
		} else {
			nodupZy = dealNodup(nodup);
		}


		List<ProfitCheckDTO> dupZy;
		if (CollectionUtil.isEmpty(isdup)) {
			dupZy = new ArrayList<>();
		} else {
			dupZy = dealIsdup1(isdup);
		}


		List<ProfitCheckDTO> afterDealAll = new ArrayList<>();
		afterDealAll.addAll(nozyData);
		afterDealAll.addAll(dupZy);
		afterDealAll.addAll(nodupZy);
		afterDealAll.forEach(f -> {
			f.setInbound(f.getInbound() == 0 ? null : f.getInbound());
			f.setSales(f.getSales() == 0 ? null : f.getSales());
			f.setLost(f.getLost() == 0 ? null : f.getLost());
			f.setRestock(f.getRestock() == 0 ? null : f.getRestock());
			f.setTransfer(f.getTransfer() == 0 ? null : f.getTransfer());
			f.setCompany(company);
		});


		return afterDealAll;
	}

	/**
	 * 处理无重复数据
	 */
	private List<ProfitCheckDTO> dealNodup(List<ProfitCheckDTO> nodupResult) {
		List<ProfitCheckDTO> newZy = new ArrayList<>();

		for (ProfitCheckDTO row : nodupResult) {
			ProfitCheckDTO dto = new ProfitCheckDTO();
			dto.setSku(row.getSku());
			dto.setInvoiceNumber(row.getInvoiceNumber());
//            dto.setInvoiceDate(row.getInvoiceDate());
			dto.setInboundDate(row.getInboundDate());
			dto.setBeginOfMonthStock(row.getBeginOfMonthStock() != null ? row.getBeginOfMonthStock() : 0);
			dto.setEndOfMonthStock(row.getEndOfMonthStock() != null ? row.getEndOfMonthStock() : 0);
			dto.setInbound(row.getInbound() != null ? row.getInbound() : 0);
			dto.setSales(row.getSales() != null ? row.getSales() : 0);
			dto.setTransfer(row.getTransfer_x() != null ? row.getTransfer_x() : 0);
			dto.setLost(row.getLost() != null ? row.getLost() : 0);
			dto.setRestock(row.getRestock() != null ? row.getRestock() : 0);
			dto.setInPriceEur(row.getInPriceEur());
			dto.setInPriceUsd(row.getInPriceUsd());
			dto.setCurrencyRate(row.getCurrencyRate());

			// 计算PLN值
			double pln = 0.0;
			if (row.getInPriceEur() != null && row.getInPriceEur() != 0.0) {
				pln = Math.round(row.getInPriceEur() * row.getCurrencyRate() * 100.0) / 100.0;
			} else if (row.getInPriceUsd() != null && row.getInPriceUsd() != 0.0) {
				pln = Math.round(row.getInPriceUsd() * row.getCurrencyRate() * 100.0) / 100.0;
			}
			dto.setPln(pln);
			dto.setType("买");

			newZy.add(dto);
		}

		return newZy;
	}

	/**
	 * 处理重复数据1
	 */
	private List<ProfitCheckDTO> dealIsdup1(List<ProfitCheckDTO> dupResult) {

		// 按 index 和 level0 排序
		dupResult = dupResult.stream().sorted(Comparator.comparing(ProfitCheckDTO::getIndex).thenComparing(ProfitCheckDTO::getLevel_0)).collect(Collectors.toList());

		dupResult.forEach(f -> {
			f.setEndOfMonthStock(null);
			if (f.getInPriceEur() != null && f.getInPriceEur() != 0.0) {
				double pln = Math.round(f.getInPriceEur() * f.getCurrencyRate() * 100.0) / 100.0;
				f.setPln(pln);
			} else {
				f.setPln(null);
			}
			if (f.getInPriceUsd() != null && f.getInPriceUsd() != 0.0) {
				double pln = Math.round(f.getInPriceUsd() * f.getCurrencyRate() * 100.0) / 100.0;
				f.setPln(pln);
			} else {
				f.setPln(null);
			}
			f.setTransfer_x(null);
			f.setInPriceEur_x(null);
			f.setCurrencyRate_x(null);
			f.setCompany(null);
		});

		return dupResult;
	}

	/**
	 * 写入CSV文件（第二流程）
	 *
	 * @param data     数据列表
	 * @param filePath 文件路径
	 */
	private void writeSecondFlowCsvFile(List<ProfitCheckDTO> data, String filePath) {
		try (PrintWriter pw = new PrintWriter(new OutputStreamWriter(Files.newOutputStream(Paths.get(filePath)), "GBK"))) {
			// 写入表头
			pw.println(String.join(",", SECOND_COLUMNS));

			// 写入数据行
			for (ProfitCheckDTO dto : data) {
				pw.println(formatSecondFlowCsvRow(dto));
			}

			log.info("数据已成功写入CSV文件: {}", filePath);
		} catch (IOException e) {
			log.error("写入CSV文件时出错: {}", filePath, e);
		}
	}

	/**
	 * 格式化第二流程CSV数据行
	 *
	 * @param item 数据项
	 * @return 格式化后的CSV行字符串
	 */
	private String formatSecondFlowCsvRow(ProfitCheckDTO item) {
		List<String> values = new ArrayList<>();
		values.add(item.getIndex() != null ? item.getIndex() : "");
		WriteCommonCVS(item, values);
		values.add(item.getLevel_0() != null ? item.getLevel_0() : "");
		values.add(String.valueOf(item.getInPriceUsd_x() != null && item.getInPriceUsd_x() > 0 ? item.getInPriceUsd_x() : ""));
		values.add(String.valueOf(item.getTransfer() != null && item.getTransfer() > 0 ? item.getTransfer() : ""));
		// 转义包含逗号或引号的字段，并用引号包围
		return values.stream().map(this::escapeCsvField).collect(Collectors.joining(","));
	}

	// 写报表CVS文件公共部分
	private void WriteCommonCVS(ProfitCheckDTO item, List<String> values) {
		values.add(item.getSku() != null ? item.getSku() : "");
		values.add(item.getInvoiceNumber() != null ? item.getInvoiceNumber() : "");
		values.add(item.getInvoiceDate() != null ? item.getInvoiceDate() : "");
		values.add(item.getInboundDate() != null ? item.getInboundDate() : "");
		values.add(String.valueOf(item.getBeginOfMonthStock() != null && item.getBeginOfMonthStock() >= 0 ? item.getBeginOfMonthStock() : ""));
		values.add(String.valueOf(item.getEndOfMonthStock() != null && item.getEndOfMonthStock() >= 0 ? item.getEndOfMonthStock() : ""));
		values.add(String.valueOf(item.getInbound() != null && item.getInbound() > 0 ? item.getInbound() : ""));
		values.add(String.valueOf(item.getSales() != null && item.getSales() > 0 ? item.getSales() : ""));
		values.add(String.valueOf(item.getTransfer_x() != null && item.getTransfer_x() > 0 ? item.getTransfer_x() : ""));
		values.add(String.valueOf(item.getLost() != null && item.getLost() > 0 ? item.getLost() : ""));
		values.add(String.valueOf(item.getRestock() != null && item.getRestock() > 0 ? item.getRestock() : ""));
		values.add(item.getInPriceEur() != null && item.getInPriceEur() > 0 ? String.valueOf(item.getInPriceEur()) : "");
		values.add(item.getInPriceUsd() != null && item.getInPriceUsd() > 0 ? String.valueOf(item.getInPriceUsd()) : "");
		values.add(item.getCurrencyRate() != null && item.getCurrencyRate() > 0 ? String.valueOf(item.getCurrencyRate()) : "");
		values.add(item.getPln() != null && item.getPln() > 0 ? String.valueOf(item.getPln()) : "");
		values.add(item.getType() != null ? item.getType() : "");
		values.add(item.getFlag() != null ? item.getFlag() : "");
	}

	/**
	 * 转义CSV字段
	 *
	 * @param field 字段值
	 * @return 转义后的字段值
	 */
	private String escapeCsvField(String field) {
		if (field == null) {
			return "";
		}
		// 如果字段包含逗号、换行符或双引号，则需要用双引号包围，并将双引号转义为两个双引号
		if (field.contains(",") || field.contains("\"") || field.contains("\n")) {
			return "\"" + field.replace("\"", "\"\"") + "\"";
		}
		return field;
	}


	// 报表入库处理


	/**
	 * 比较报表数据和库存数据是否一致
	 */
	public void checkProfitKc(String date) {

		String name = "profitCheckInsert-05";
		DailyAuditStatus dailyAuditStatus = statusMapper.selectStatus(name, date);
		List<CheckProfitBO> checkProfitList = profitCheckMapper.getCheckProfitList(date);
		checkProfitList.forEach(f -> f.setBelongs(f.getBelongs().toUpperCase()));
		List<CheckProfitBO> checkKcList = tempKcjlDailyMapper.getCheckKcList(date);
		Map<String, Integer> kcMap = checkKcList.stream().collect(Collectors.toMap(l ->
				l.getSku() + l.getBelongs() + l.getDate(), CheckProfitBO::getNum));
		for (CheckProfitBO bo : checkProfitList) {
			String key = bo.getSku() + bo.getBelongs() + bo.getDate();
			Integer num = kcMap.get(key);
			if (num == null) {
				log.error("此库存数据不存在：{}", bo);
				XxlJobHelper.log("此库存数据不存在：{}", bo);
				throw new RuntimeException("此库存数据不存在：" + bo);
			}
			if (!Objects.equals(bo.getNum(), num)) {
				log.error("此数据的库存不一致,库存记录num的值为:{}，报表的num值为：{}，报表数据为{}", num, bo.getNum(), bo);
				XxlJobHelper.log("此数据的库存不一致,库存记录num的值为:{}，报表的num值为：{}，报表数据为{}", num, bo.getNum(), bo);
				throw new RuntimeException("此数据的库存不一致,库存记录num的值为:" + num + " ，报表的num值为：" + bo.getNum() + "报表的数据为：" + bo);
			}
		}
		log.info("【 {} 】利润检查表数据校验通过", date);
		XxlJobHelper.log("【 {} 】利润检查表数据校验通过", date);

		// 修改执行状态
		commonService.UpdateOrInsertStatus(dailyAuditStatus, name, date);
	}

	/**
	 * 读取利润检查表数据并生成CVS文件
	 *
	 * @param date
	 */
	public void getDataByDate(String date) {
		List<ProfitCheckDTO> dataByDate = profitCheckMapper.getDataByDate(date);

		String fileName = date.replace("-", "") + "利润检查表.csv";
		String filePath = commonService.joinDate(PATH, date) + "/" + fileName;

		try (PrintWriter pw = new PrintWriter(new OutputStreamWriter(Files.newOutputStream(Paths.get(filePath)), "GBK"))) {
			// 写入表头
			pw.println(String.join(",", PROFIT_COLUMNS));

			// 写入数据行
			for (ProfitCheckDTO dto : dataByDate) {
				List<String> values = new ArrayList<>();
				WriteCommonCVS(dto, values);
				values.add(dto.getCompany() != null ? dto.getCompany() : "");
				pw.println(values.stream().map(this::escapeCsvField).collect(Collectors.joining(",")));
			}

			log.info("数据已成功写入CSV文件: {}", filePath);
			XxlJobHelper.log("数据已成功写入CSV文件: {}", filePath);
		} catch (IOException e) {
			log.error("写入CSV文件时出错: {}", filePath, e);
			XxlJobHelper.log("写入CSV文件时出错: {}", filePath, e);
		}

	}


	/**
	 * 处理后的报表数据入库
	 *
	 * @param date 入指定时间的数据
	 */
	public void insertData(String date) {

		try {

			String name = "profitCheckInsert-05";
			DailyAuditStatus dailyAuditStatus = statusMapper.selectStatus(name, date);
			if (dailyAuditStatus != null && dailyAuditStatus.getFlag() == 1) {
				log.info("{} - 该方法已执行，进行跳过！", name);
				return;
			}

			//当代码走到这里，说明该方法未执行或者执行失败后的重试,需要清理当天的数据，重新运行
			profitCheckMapper.deleteProfitCheckDaily(date);
			profitCheckMapper.deleteRecord(date);
			log.info("{}日的数据清除完毕！", date);


			// 获取当前代理对象
			ProfitCheck proxy = (ProfitCheck) AopContext.currentProxy();
			for (String company : COMPANY_LIST) {

				List<ProfitCheckDTO> List = commonService.readCsvDataFromProfitCheck(date, company);
				List<ProfitStatement> allData = profitCheckMapper.getAllData(company);

				if (CollectionUtil.isEmpty(allData)) {
					proxy.batchInsert(List, company, date);
				} else {
					Map<String, ProfitStatement> allKeys = allData.stream().collect(Collectors.toMap(f -> f.getSku() + "|" + f.getInvoiceNo(), f -> f));
					Map<Boolean, List<ProfitCheckDTO>> partitioned = List.stream()
							.collect(Collectors.partitioningBy(f -> allKeys.containsKey(f.getSku() + "|" + f.getInvoiceNumber())));

					// 存在相同的数据 - 这里指的是相同的sku和发票号
					List<ProfitCheckDTO> existKeysList = partitioned.get(true);
					List<ProfitCheckDTO> noExistKeysList = partitioned.get(false);

					if (CollectionUtil.isNotEmpty(existKeysList)) {
						proxy.UpdateAndInsert(company, date, existKeysList, allKeys);
					}

					if (CollectionUtil.isNotEmpty(noExistKeysList)) {
						proxy.batchInsert(noExistKeysList, company, date);
					}

				}
			}

			log.info("{}日的报表数据生成完毕！", date);
			XxlJobHelper.log("{}日的报表数据生成完毕！", date);
		} catch (Exception e) {
			log.info("{}日的报表数据生成失败！", date);
			XxlJobHelper.log("{}日的报表数据生成失败！", date);
		}

	}

	/**
	 * 更新利润总数据并新增日数据、报表
	 */
	@Transactional
	@DS("fa_eu_daily")
	public void UpdateAndInsert(String company, String date, List<ProfitCheckDTO> existKeysList, Map<String, ProfitStatement> allKeys) {
		List<ProfitStatement> profitCheckList = new LinkedList<>();
		List<ProfitCheckRecord> profitCheckRecordList = new LinkedList<>();
		List<ProfitStatementDaily> profitCheckDailyList = new LinkedList<>();
		Map<String, List<ProfitCheckDTO>> profitCheckMap = existKeysList.stream()
				.collect(Collectors.groupingBy(p -> p.getSku() + "|" +
						(StringUtils.isNotBlank(p.getInvoiceNumber()) ? p.getInvoiceNumber() : "")));
		profitCheckMap.remove("null");

		profitCheckMap.forEach((k, v) -> {
			String id = allKeys.get(k).getId();
			dealProfitDailyAndRecord(company, date, v, id, profitCheckRecordList, profitCheckDailyList, profitCheckList);
		});

		// 确保在数据更新前检查表是否存在，避免运行时错误
		if (!profitCheckList.isEmpty()) {
			profitCheckMapper.batchUpdateProfitCheck(profitCheckList);
		}
		if (!profitCheckDailyList.isEmpty()) {
			profitCheckMapper.batchSaveProfitCheckDaily(profitCheckDailyList);
		}
		if (!profitCheckRecordList.isEmpty()) {
			profitCheckMapper.batchSaveRecord(profitCheckRecordList);
		}
	}

	/**
	 * 新增利润总数据、日数据、报表
	 */
	@Transactional
	@DS("fa_eu_daily")
	public void batchInsert(List<ProfitCheckDTO> List, String company, String date) {
		List<ProfitStatement> profitCheckList = new LinkedList<>();
		List<ProfitCheckRecord> profitCheckRecordList = new LinkedList<>();
		List<ProfitStatementDaily> profitCheckDailyList = new LinkedList<>();
		Map<String, List<ProfitCheckDTO>> profitCheckMap = List.stream()
				.collect(Collectors.groupingBy(p -> p.getSku() +
						(StringUtils.isNotBlank(p.getInvoiceNumber()) ? p.getInvoiceNumber() : "")));
		profitCheckMap.remove("null");
		profitCheckMap.forEach((k, v) -> {
			String id = String.valueOf(ID_GENERATOR.incrementAndGet());
			dealProfitDailyAndRecord(company, date, v, id, profitCheckRecordList, profitCheckDailyList, profitCheckList);
		});

		// 确保在数据插入前检查表是否存在，避免运行时错误
		if (!profitCheckList.isEmpty()) {
			profitCheckMapper.batchSaveProfitCheck(profitCheckList);
		}
		if (!profitCheckDailyList.isEmpty()) {
			profitCheckMapper.batchSaveProfitCheckDaily(profitCheckDailyList);
		}
		if (!profitCheckRecordList.isEmpty()) {
			profitCheckMapper.batchSaveRecord(profitCheckRecordList);
		}
	}

	/**
	 * 处理利润日数据和报表数据数据
	 *
	 * @param company               货权
	 * @param date                  当天时间
	 * @param v                     sku+invcoiceNo数据集
	 * @param id                    唯一id
	 * @param profitCheckRecordList 报表集合
	 * @param profitCheckDailyList  日利润明细集合
	 * @param profitCheckList       利润表集合
	 */
	private static void dealProfitDailyAndRecord(String company, String date, List<ProfitCheckDTO> v, String id, List<ProfitCheckRecord> profitCheckRecordList, List<ProfitStatementDaily> profitCheckDailyList, List<ProfitStatement> profitCheckList) {

		if (!v.isEmpty()) {

			int remainingNum = v.stream()
					.filter(dto -> dto.getEndOfMonthStock() != null)  // 过滤掉空值
					.mapToInt(ProfitCheckDTO::getEndOfMonthStock)
					.sum();

			int beginStockNum = v.stream()
					.filter(dto -> dto.getBeginOfMonthStock() != null)  // 过滤掉空值
					.mapToInt(ProfitCheckDTO::getBeginOfMonthStock)
					.sum();

			ProfitStatement profitCheck = ProfitStatement.builder()
					.id(id)
					.sku(v.get(0).getSku())
					.invoiceNo(v.get(0).getInvoiceNumber())
					.belongs(company)
					.date(date)
					.remainingNum(remainingNum)
					.createTime(LocalDateTime.now()).build();

			ProfitStatementDaily profitCheckDaily = ProfitStatementDaily.builder()
					.profitCheckId(id)
					.date(date)
					.flag(beginStockNum != remainingNum)
					.beginStockNum(beginStockNum)
					.endStockNum(remainingNum).build();

			// 为record进行分组，现已入货时间为准，再优先以pln 因为pln是根据eur+rate/usd+rate得来的，几乎不存在不同的数据混在一起
			Map<String, List<ProfitCheckDTO>> recordMap = v.stream()
					.collect(Collectors.groupingBy(dto -> {
						if (dto.getInboundDate() != null) {
							return dto.getInboundDate();
						} else if (dto.getPln() != null) {
							return dto.getPln().toString();
						} else if (dto.getInPriceEur() != null) {
							return dto.getInPriceEur() + "|" + dto.getCurrencyRate();
						} else if (dto.getInPriceUsd() != null) {
							return dto.getInPriceUsd() + "|" + dto.getCurrencyRate();
						} else if (dto.getCurrencyRate() != null) {
							return dto.getCurrencyRate().toString();
						}
						return "null"; // 默认值
					}));
			if (recordMap.containsKey("null")) {
				log.error("存在异常数据：{}", recordMap.get("null"));
				throw new RuntimeException("存在异常数据" + recordMap.get("null"));
			}

			recordMap.forEach((k1, l1) -> {
				int endStockNum1 = l1.stream().mapToInt(d -> d.getEndOfMonthStock() != null ? d.getEndOfMonthStock() : 0).sum();
				int beginStockNum1 = l1.stream().mapToInt(d -> d.getBeginOfMonthStock() != null ? d.getBeginOfMonthStock() : 0).sum();
				int inBoundNum = l1.stream().mapToInt(d -> d.getInbound() != null ? d.getInbound() : 0).sum();
				int salesNum = l1.stream().mapToInt(d -> d.getSales() != null ? d.getSales() : 0).sum();
				int transferNum = l1.stream().mapToInt(d -> d.getTransfer() != null ? d.getTransfer() : 0).sum();
				int lostNum = l1.stream().mapToInt(d -> d.getLost() != null ? d.getLost() : 0).sum();
				int restockNum = l1.stream().mapToInt(d -> d.getRestock() != null ? d.getRestock() : 0).sum();

				ProfitCheckRecord record = ProfitCheckRecord.builder()
						.profitCheckId(id).
						invoiceDate(l1.get(0).getInvoiceDate() != null ? l1.get(0).getInvoiceDate() : "").
						inboundDate(l1.get(0).getInboundDate() != null ? l1.get(0).getInboundDate() : "").
						beginStockNum(beginStockNum1).
						endStockNum(endStockNum1).
						inbound(inBoundNum).
						sales(salesNum).
						transfer(transferNum).
						lost(lostNum).
						restock(restockNum).
						inPriceEur(l1.get(0).getInPriceEur()).
						inPriceUsd(l1.get(0).getInPriceUsd()).
						currencyRate(l1.get(0).getCurrencyRate()).
						pln(l1.get(0).getPln()).
						type(l1.get(0).getType()).
						date(date).build();
				profitCheckRecordList.add(record);
			});

			profitCheckDailyList.add(profitCheckDaily);
			profitCheckList.add(profitCheck);
		}

	}

}
